package cn.edu.nju.ws.sview.views;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Properties;

import cn.edu.nju.ws.sview.cache.URIIDCache;
import cn.edu.nju.ws.sview.cache.URILabelCache;
import cn.edu.nju.ws.sview.database.DBConnectionFactory;
import cn.edu.nju.ws.sview.rdf.ResourceFactory;
import cn.edu.nju.ws.sview.rdf.URIResource;
import cn.edu.nju.ws.sview.util.URIUtil;
/**
 * 自定义实体，包括单实体或实体列表
 * @author ssgong
 */
public abstract class Entity{
	public static final int CUSTOM_ENTITY;
	public static final int URI_ENTITY ;
	public static final int COLLECTION;
	static{
		Properties properties = new Properties();
		try {
			properties.load(ResourceFactory.class.getClassLoader().getResourceAsStream("conf.properties")) ;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		CUSTOM_ENTITY = Integer.parseInt(properties.getProperty("custom_entity"));
		URI_ENTITY = Integer.parseInt(properties.getProperty("uri_entity"));
		COLLECTION = Integer.parseInt(properties.getProperty("collection"));
		properties.clear();
	}
	
	public final static String URI_ENTITY_Str = "o" ;
	public final static String CUSTOM_ENTITY_Str = "e";
	public final static String COLLECTION_Str="c";
	public int id;
	
	/**
	 * 将实体类型字符串转成对应ID
	 * @param typeStr
	 * @return
	 */
	public static int transTypeStr2ID(String typeStr){
		int result = -1;
		if(typeStr.equals(COLLECTION_Str))
			result = COLLECTION;
		else if(typeStr.equals(CUSTOM_ENTITY_Str))
			result = CUSTOM_ENTITY;
		else if(typeStr.equals(URI_ENTITY_Str))
			result = URI_ENTITY;
		return result;
	}
	
	/**
	 * 将实体类型ID转成对应字符串
	 * @param typeID
	 * @return
	 */
	public static String transTypeID2Str(int typeID){
		String result = null;
		if(typeID == COLLECTION)
			result = COLLECTION_Str;
		else if(typeID== CUSTOM_ENTITY)
			result = CUSTOM_ENTITY_Str ;
		else if(typeID ==URI_ENTITY)
			result = URI_ENTITY_Str;
		return result;
	}
	
	private String name = null;
	private String lang = null;
	private String group = null;
	//groupID =- 1, 并且是集合时标明这是一个临时的集合
	private int groupID = 0; 
	private HashSet<URIResource> members = null;
	
	protected Entity(String name,String group,String lang,HashSet<URIResource> members) {
		this.name = name;
		this.group = group;
		this.lang = lang;
		this.members = members;
	}
	
	protected Entity(String name,int group_id,String lang,HashSet<URIResource> members) {
		this.name = name;
		this.groupID = group_id;
		this.lang = lang;
		this.members = members;
	}

	public void setID(int id){
		this.id = id;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public void setUriEntities(HashSet<URIResource> uriEntities) {
		this.members = uriEntities;
	}
	
	public void setLang(String lang) {
		this.lang = lang;
	}
	
	public void setGroup(String group) {
		this.group = group;
	}
	
	/**
	 * must set id ahead
	 * @return
	 */
	public int getID(){
		return this.id;
	}
	
	/**
	 * get the name of entity of specific lang
	 * @param id. the entity id
	 * @return null if invalid entity id or none for tmp collection
	 */
	public static String getNameByEntityID(int id,String lang) {
		if(lang==null)
			lang="";
		String name = null;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			Statement stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery(String.format(
					"select name " +
					"from identity_label " +
					"where entity_id = %d and lang='%s'", id,lang));
			if ( rs.next() ) {
				name = rs.getString("name") ;
			}
			rs.close();
			
			if(name==null){
				//可能特定语言的名字不存在，拿其它的代替
				rs = stmt.executeQuery(String.format(
						"select name " +
						"from identity_label " +
						"where entity_id = %d ", id));
				if(rs.next()){
					name = rs.getString("name");
				}
				rs.close();
			}
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		return name;
	}

	/**
	 * get uri entities of the entity
	 * @return
	 */
	public HashSet<URIResource> getMembers() {
		return members;
	}
	
	/**
	 * get the entity's members
	 * @param entityId. the entity id
	 * @return. the entity's members
	 */
	public static HashSet<Integer> getMembersByEntityId(int entityId){
		HashSet<Integer> result = new HashSet<Integer>();
		try {
			Connection conn = DBConnectionFactory.getConnection();
			Statement stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery(String.format(
					"select uri_id " +
					"from identity_entity " +
					"where identity_id = %d", entityId));
			while ( rs.next() ) {
				result.add(rs.getInt("uri_id"));
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	
	/**
	 * 返回包含特定uri的限定类型的实体
	 * @param uriId
	 * @param 限定返回实体的类型 itemType itemType = 1, 3:自定义实体，collection  itemType = 0:不限定返回实体类型
	 * @return 
	 */
	public static HashSet<Integer> getEntitiesHavingURIByBookmark(int uriId, int itemType, String lang){
		HashSet<Integer> result = new HashSet<Integer>();
		String typeRestrict;
		if(itemType == 0)
			typeRestrict = "";
		else if(itemType == 2)
			typeRestrict = " AND identity.type = 1";
		else 
			typeRestrict = " AND identity.type = " + itemType;
		try{
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement ps = conn.prepareStatement("SELECT identity_entity.identity_id FROM identity_entity, identity, bm_entity, bookmark " +
					"WHERE identity_entity.uri_id = ? AND identity_entity.identity_id = identity.id AND identity_entity.identity_id = bm_entity.entity_id " +
					" AND bm_entity.bm_id = bookmark.id AND bookmark.lang = ?" + typeRestrict + " ;");
			ps.setInt(1, uriId);
			ps.setString(2, lang);
			ResultSet rs = ps.executeQuery();
			while(rs.next()){
				int itemId = rs.getInt("identity_entity.identity_id");
				result.add(itemId);
			}
			rs.close();
			ps.close();
			conn.close();
		}catch(SQLException e){
			e.printStackTrace();   
		}
		return result;       
	}
	
	/**
	 * get entity id by name with group
	 * @param name. the entity name
	 * @param group. the group name
	 * @param lang. the lang of the name
	 * @return. -1 if the group has no entity with the name
	 */
	public static int getEntityIdByNameGroupLang(String name, String group,String lang) {
		int entityId = -1;
		int gid = Group.getGroupIdByName(group);
		if ( gid < 0 ) return -1;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement stmt = conn.prepareStatement("select entity_id from identity_label where group_id = ? and name =? and lang=?");
			stmt.setInt(1, gid);
			stmt.setString(2, name);
			stmt.setString(3, lang);
			ResultSet rs = stmt.executeQuery();
			if ( rs.next() ) {
				entityId = rs.getInt("entity_id");
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return entityId;
	}

	/**
	 * get entity id by name with group. 
	 * 对于临时集合，禁用此方法
	 * @param name. the entity name
	 * @param groupID. the group ID
	 * @param lang. the lang of the name
	 * @return. -1 if the group has no entity with the name
	 */	
	public static int getEntityIdByNameGroupIDLang(String name, int groupID,String lang) {
		int entityId = -1;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement stmt = conn.prepareStatement("select entity_id from identity_label where group_id =? and name = ? and lang=?");
			stmt.setInt(1, groupID);
			stmt.setString(2, name);
			stmt.setString(3, lang) ;
			ResultSet rs = stmt.executeQuery();
			if ( rs.next() ) {
				entityId = rs.getInt("entity_id");
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return entityId;
	}
	
	public String getGroup() {
		return group;
	}
	
	public String getLang() {
		return lang;
	}
	
	/**
	 * 判断collectionID标识的集合是否是临时集合
	 * @param collectionID
	 * @return
	 */
	public static boolean isTmpCollection(int etype,int collectionID){
		if(etype!=Entity.COLLECTION)
			throw new IllegalArgumentException("This entity is not collection.");
		boolean result = false;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement stmt = conn.prepareStatement("select group_id from identity where id=?");
			stmt.setInt(1, collectionID) ;
			ResultSet rs = stmt.executeQuery();
			if ( rs.next() ) {
				int gid = rs.getInt("group_id");
				if(gid==-1){
					result = true;
				}
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	/**
	 * 判断以entityID标识的自定义实体是否存在
	 * @param entityID
	 * @return
	 */
	public static boolean isThere(int entityID){
		boolean result = false;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement stmt = conn.prepareStatement("select null from identity where id=?");
			stmt.setInt(1, entityID) ;
			ResultSet rs = stmt.executeQuery();
			if ( rs.next() ) {
				result = true;
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return result;
	}
	
   /**
    * 得到创建entity的group id。
    * @param entityID. the id of entity
    * @return -10 if invalid
    */
	public static int getGroupIDOfEntity(int entityID){
		int groupID = -10;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement stmt = conn.prepareStatement("select group_id from identity where id=?");
			stmt.setInt(1, entityID) ;
			ResultSet rs = stmt.executeQuery();
			if ( rs.next() ) {
				groupID = rs.getInt("group_id");
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return groupID;
	}
	
	public static String getLatestUpdateTime(int customEntityID){
		String time = null;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement stmt = conn.prepareStatement("select update_time from identity where id=?");
			stmt.setInt(1, customEntityID) ;
			ResultSet rs = stmt.executeQuery();
			if ( rs.next() ) {
				Timestamp date = rs.getTimestamp("update_time");
				SimpleDateFormat format =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
				time =format.format(date);
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return time;
	}
	
	/**
	 * get the type of entity(custom_entity, collection)
	 * @param entityId
	 * @return the type of entity
	 */
	public static int getTypeById(int entityId){
		int type=-1;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement stmt = conn
			  .prepareStatement("select type from identity where id=?") ;
			stmt.setInt(1, entityId) ;
			ResultSet rs = stmt.executeQuery();
			if ( rs.next() ) {
				type = rs.getInt("type") ;
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return type;
	}
	
	/**
	 * serialize the new created entity 
	 * @return false if the group has entity with the same name
	 */
	public boolean serializeNew(){
		int gid = Group.getGroupIdByName(group);
		if ( gid < 0 ) return false;
		boolean flag = false;
		int entityId = getEntityIdByNameGroupLang(name, group,lang);
		if(entityId>0)
			throw new IllegalArgumentException("The custom entity has existed!");
		try {
			Connection conn = DBConnectionFactory.getConnection();
			int type = (this instanceof CustomEntity) ? Entity.CUSTOM_ENTITY
					: Entity.COLLECTION;

			Statement stmt = conn.createStatement();
			stmt.executeUpdate(String.format(
					"insert into identity(group_id,type,create_time,update_time) "
							+ "values(%d, %d,NOW(),NOW())", gid, type));
			ResultSet rs = stmt.executeQuery("select last_insert_id() as id");
			if (rs.next()) {
				entityId = rs.getInt("id");
			}
			rs.close();
			stmt.close();
			conn.close();
			addOrUpdateLangName(entityId, this.name, this.lang);
			updateMembers(entityId, this.members);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return flag;
	}
	
	/**
	 * 存储临时集合
	 * @return
	 */
	public void serializeTmpCollection(){
		if(this.groupID!=-1){
			throw new IllegalArgumentException("临时集合的groupID必须为-1");
		}
		int entityId = 0;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			int type =  Entity.COLLECTION;

			Statement stmt = conn.createStatement();
			stmt.executeUpdate(String.format(
					"insert into identity(group_id,type,create_time,update_time) "
							+ "values(%d, %d,NOW(),NOW())", this.groupID, type));
			ResultSet rs = stmt.executeQuery("select last_insert_id() as id");
			if (rs.next()) {
				entityId = rs.getInt("id");
			}
			rs.close();
			stmt.close();
			conn.close();
			addOrUpdateLangName(entityId, this.name, this.lang);
			updateMembers(entityId, this.members);
			this.setID(entityId);
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 对已经创建的自定义实体增加或者更新它的一个语言下的名字
	 * @param entityID.自定义实体id
	 * @param name.新的名字
	 * @param lang.语言
	 */
	public static void addOrUpdateLangName(int entityID, String name, String lang){
		if(!Entity.isThere(entityID)) return;
		int groupID = Entity.getGroupIDOfEntity(entityID);
		try {
			Connection conn = DBConnectionFactory.getConnection();
			PreparedStatement ps = conn.prepareStatement("insert into identity_label(entity_id,group_id,name,lang) values(?,?,?,?) on duplicate key " +
					"update name=values(name)");
			ps.setInt(1, entityID);
			ps.setInt(2,groupID);
			ps.setString(3, name);
			ps.setString(4, lang);
			ps.executeUpdate();
			ps.close();
			Statement stmt = conn.createStatement();
			stmt.executeUpdate("update identity set update_time=NOW() where id ="+entityID);
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 对已经创建的自定义实体增加或者更新它的一个语言下的名字
	 * @param entityID.自定义实体id
	 * @param name.新的名字
	 * @param lang.语言
	 */
	public static void updateMembers(int entityId,HashSet<URIResource> urimembers){
		if(!Entity.isThere(entityId)) return;
		try {
			Connection conn = DBConnectionFactory.getConnection();
			Statement delstmt = conn.createStatement();
			delstmt.executeUpdate(String.format("delete from identity_entity where identity_id=%d",entityId));
			delstmt.close();
			PreparedStatement pstmt = conn.prepareStatement(
					"insert  into identity_entity(identity_id, uri_id) " +
					"values(?, ?)");
			pstmt.setInt(1, entityId);
			for (URIResource uri : urimembers) {
				pstmt.setInt(2, URIIDCache.getInstance().getURIID(uri.getURI(), true));
				pstmt.executeUpdate();
			}
			pstmt.close();

			Statement stmt = conn.createStatement();
			stmt.executeUpdate("update identity set update_time=NOW() where id ="
					+ entityId);
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * store the class types of which the entity belongs to
	 * @param id. entity id
	 * @param classIDs. class ids
	 */
	public static void storeClassTypesById(int id, ArrayList<Integer> classIDs){
		try {
			Connection conn = DBConnectionFactory.getConnection();
			Statement delstmt = conn.createStatement();
			delstmt.executeUpdate("delete from identity_type where identity_id="+id);
			delstmt.close();
			PreparedStatement stmt = conn.prepareStatement(
					"insert into identity_type(identity_id,class_id) values(?,?) ");
		    for(Integer classId:classIDs){
		    	stmt.setInt(1, id);
		    	stmt.setInt(2, classId);
		    	stmt.executeUpdate();
		    }
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
	
	/** 
	 * get the class type of which the custom  entity belongs to
	 * @param id.entity id
	 * @param lang
	 * @return
	 */
	public static ArrayList<Entry<Integer, String>> getClassTypesById(int id,String lang) {
		ArrayList<Entry<Integer, String>> types = new ArrayList<Entry<Integer,String>>();
		try {
			Connection conn = DBConnectionFactory.getConnection();
			Statement stmt = conn.createStatement();
			ResultSet rs = stmt.executeQuery(String.format(
					"select class_id " +
		    		"from identity_type " +
		    		"where identity_id = %d", id));
			while ( rs.next() ) {
				int typeId = rs.getInt("class_id");
				String typeLabel = null;
				try {
					typeLabel = URILabelCache.getInstance().getLabel(typeId, lang);
					if(typeLabel.equals("")){
						String uri = URIIDCache.getInstance().getURI(typeId) ;
						typeLabel = URIUtil.getLocalname(uri) ;
					}
				} catch (Throwable e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				types.add(new SimpleEntry<Integer, String>(typeId, typeLabel));
			}
			rs.close();
			stmt.close();
			conn.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return types;
	}
	
	/**
	 * 删除与一个custom entity相关的信息
	 * @param etype
	 * @param eid
	 */
	public static void delete(int etype,int eid){
		Connection connection = DBConnectionFactory.getConnection();
		
		try {
			Statement stmt = connection.createStatement();
			ResultSet rs = null;
			
			//delete comment
			rs= stmt.executeQuery(String.format("select id from comment where member_type=%d and  member_id=%d",etype,eid ));
			while(rs.next()){
				int id = rs.getInt("id");
			    Comment.delete(id) ;
			}
			rs.close();
			
			//delete img
			rs= stmt.executeQuery(String.format("select id from img where etype=%d and  eid=%d",etype,eid ));
			while(rs.next()){
				int id = rs.getInt("id");
			    Image.delete(id) ;
			}
			rs.close();
			
			//delete visit lang
			stmt.executeUpdate(String.format("delete from visit_lang where member_type=%d and member_id=%d", etype,eid));
			
			//delete day visit
			stmt.executeUpdate(String.format("delete from user_day_visit where member_type=%d and member_id=%d", etype,eid));
			
			//delete total visit
			stmt.executeUpdate(String.format("delete from user_visit where member_type=%d and member_id=%d", etype,eid));
			
			//delete identity
			stmt.executeUpdate(String.format("delete from identity where id=%d",eid));
			stmt.executeUpdate(String.format("delete from identity_entity where identity_id=%d",eid));
			stmt.executeUpdate(String.format("delete from identity_label where entity_id=%d",eid));
			stmt.executeUpdate(String.format("delete from identity_type where identity_id=%d",eid));
			
			stmt.close();
			connection.close();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static class EntityItem{
		private String etype;
		private int eid;
		public EntityItem(String etype,int eid){
			this.etype = etype;
			this.eid = eid;
		}
		
		public String getEType(){
			return this.etype;
		}
		
		public int getEID(){
			return this.eid;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + eid;
			result = prime * result + ((etype == null) ? 0 : etype.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			EntityItem other = (EntityItem) obj;
			if (eid != other.eid)
				return false;
			if (etype == null) {
				if (other.etype != null)
					return false;
			} else if (!etype.equals(other.etype))
				return false;
			return true;
		}

	}
}
